Recoil > Loadable
mapなどのインタフェースが生えていて"値"と"計算"を区別できる
ReduxやRxで原始的に実装しようとするとコード量が大変なことになる 状態を型安全にハンドリングできる
この例では、複数のレイヤーを持つグラフをレンダリングします。
各レイヤーは、潜在的に高価なデータクエリを持っています。
まだ保留中の各レイヤーのスピナーを使用してすぐにチャートをレンダリングし、そのレイヤーのデータが届くと、チャートを更新して各新規レイヤーを追加します。
もしクエリにエラーがあるレイヤーがあれば、そのレイヤーだけがエラーメッセージを表示し、残りのレイヤーはレンダリングを継続します。
code:typescript
function MyChart({layerQueries}: {layerQueries: Array<RecoilValue<Layer>>}) {
// layerQueriesがデータフェッチを行う非同期処理
// waitForNoneは実行時のコンテキストを引き継いでSelectorの状態をLoadableで返す関数
// useRecoilValueLoadable(layerQueries)でもいいはずなのだが、サンプルがなぜこういう書き方なのかは謎
const layerLoadables = useRecoilValue(waitForNone(layerQueries));
return (
<Chart>
{layerLoadables.map((layerLoadable, i) => {
switch (layerLoadable.state) {
// 非同期処理の結果が返ってきたとき
case 'hasValue':
return <Layer key={i} data={layerLoadable.contents} />;
// 非同期処理の結果がエラーなとき
case 'hasError':
return <LayerErrorBadge key={i} error={layerLoadable.contents} />;
// 非同期処理中
case 'loading':
return <LayerWithSpinner key={i} />;
}
})}
</Chart>
);
}
Loadable.getValueでSuspenseモードで使ってもいいし、上述のLoadable.stateによってViewを切り替えてもいい Loadable.mapつかうとインラインにtransform的な実装が出来る
code:.ts
const usersWithCount = useRecoilValueLoadable(usersState).map(users => {
return {
data: users,
count: users.length
}
}).getValue()
// ->
usersWithCount.data
usersWithCount.count